home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / flockRichDNDService.js < prev    next >
Text File  |  2007-10-18  |  33KB  |  1,006 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16.  
  17. const CC = Components.classes;
  18. const CI = Components.interfaces;
  19. const CR = Components.results;
  20.  
  21. Components.utils.import("resource:///modules/FlockXPCOMUtils.jsm");
  22. FlockXPCOMUtils.debug = false;
  23.  
  24. const MODULE_NAME = "Flock Rich Drag and Drop Service";
  25.  
  26. // The Rich Drag&Drop service
  27. const FLOCK_RICHDRAGDROP_SERVICE_CLASS_NAME
  28.         = "Flock Rich Drag and Drop Service";
  29. const FLOCK_RICHDRAGDROP_SERVICE_CLASS_ID
  30.         = Components.ID("{30b8dc92-abe0-44ee-a845-e1a827d59610}");
  31. const FLOCK_RICHDRAGDROP_SERVICE_CONTRACT_ID = "@flock.com/rich-dnd-service;1";
  32. const FLOCK_PREF_SERVICE_CONTRACT_ID = "@mozilla.org/preferences-service;1";
  33. const FLOCK_CATEGORY_MANAGER_CONTRACT_ID = "@mozilla.org/categorymanager;1";
  34. const FLOCK_RICH_CONTENT_CATEGORY_ENTRY = "flockRichContentHandler";
  35. const FLOCK_BREADCRUMB_ID = "flock-breadcrumb";
  36.  
  37. const FLAVOR_UNICODE = "text/unicode";
  38. const FLAVOR_HTML = "text/html";
  39. const FLAVOR_MOZ_URL = "text/x-moz-url";
  40. const FLAVOR_FLOCK_MEDIA = "text/x-flock-media";
  41. const FLAVOR_TEXT_CITE = "text/x-flock-textcitation";
  42. const FLAVOR_HTML_CITE = "text/x-flock-htmlcitation";
  43. const FLAVOR_MOZ_RDF = "moz/rdfitem";
  44.  
  45. // From viewPartialSource.js
  46. //
  47. // These are markers used to delimit the selection during processing. They
  48. // are removed from the final rendering, but we pick space-like characters for
  49. // safety (and futhermore, these are known to be mapped to a 0-length string
  50. // in transliterate.properties). It is okay to set start=end, we use findNext()
  51. // U+200B ZERO WIDTH SPACE
  52. const MARK_SELECTION_START = "\u200B\u200B\u200B\u200B\u200B";
  53. const MARK_SELECTION_END = "\u200B\u200B\u200B\u200B\u200B";
  54.  
  55. const NS_XHTML = "http://www.w3.org/1999/xhtml";
  56.  
  57. var _logger = CC["@flock.com/logger;1"].createInstance(CI.flockILogger);
  58. _logger.init("flockRichDragDropService");
  59.  
  60. /**************************************************************************
  61.  * Component: Flock Rich Drag and Drop Service
  62.  **************************************************************************/
  63.  
  64. // Constructor.
  65. function flockRichDragDropService() {
  66. }
  67.  
  68. /**************************************************************************
  69.  * Flock Rich Drag and Drop Service: XPCOM Component Creation
  70.  **************************************************************************/
  71.  
  72. flockRichDragDropService.prototype = new FlockXPCOMUtils.genericComponent(
  73.   FLOCK_RICHDRAGDROP_SERVICE_CLASS_NAME,
  74.   FLOCK_RICHDRAGDROP_SERVICE_CLASS_ID,
  75.   FLOCK_RICHDRAGDROP_SERVICE_CONTRACT_ID,
  76.   flockRichDragDropService,
  77.   CI.nsIClassInfo.SINGLETON,
  78.   [
  79.     CI.flockIRichDNDService
  80.   ]
  81. );
  82.  
  83. // FlockXPCOMUtils.genericComponent() registration callbacks.
  84. flockRichDragDropService.prototype.register =
  85. function flockRichDragDropService_register(aCompMgr, aFileSpec, aLocation, aType) {
  86. }
  87.  
  88. flockRichDragDropService.prototype.unregister =
  89. function flockRichDragDropService_unregister(aCompMgr, aFileSpec, aLocation, aType) {
  90. }
  91.  
  92. /**************************************************************************
  93.  * Flock Rich Drag and Drop Service: flockIRichDNDService Implementation
  94.  **************************************************************************/
  95.  
  96. flockRichDragDropService.prototype.getRichSelection =
  97. function fRDDS_getRichSelection(aSession) {
  98.   var node = aSession.sourceNode;
  99.   // Dragging from outside the browser
  100.   if (!node) {
  101.     return null;
  102.   }
  103.  
  104.   // Gather some information about the current window, as it can be used
  105.   // to enrich the data we will be sending
  106.   var wm = CC["@mozilla.org/appshell/window-mediator;1"]
  107.            .getService(CI.nsIWindowMediator);
  108.   var top = wm.getMostRecentWindow("navigator:browser");
  109.   var contentElement = top.document.getElementById("content");
  110.   var topUrl = contentElement.contentDocument.URL;
  111.   var topTitle = contentElement.contentTitle;
  112.   if (topTitle.length == 0) {
  113.     topTitle = contentElement.contentDocument.location;
  114.   }
  115.   if (topUrl && topUrl.match(/^chrome:\/\/.+/)) {
  116.     // Never mark content with a chrome URL, as it is likely to be send
  117.     // to someone or published online
  118.     topUrl = "";
  119.   }
  120.  
  121.   var nodeName = node.nodeName.toLowerCase();
  122.   _logger.debug("nodeName="+nodeName);
  123.  
  124.   // HTML image from a web page
  125.   if (nodeName == "img") {
  126.     var imageTitle = "";
  127.     if (node.title) {
  128.       imageTitle = node.title;
  129.     } else if (node.alt) {
  130.       imageTitle = node.alt;
  131.     }
  132.     var imageURL = this._makeURLAbsolute(topUrl, node.src);
  133.     // Don't insert a stupid image containing Javascript
  134.     if (imageURL.match("javascript:")) {
  135.       return null;
  136.     }
  137.     if (imageURL == topUrl) {
  138.       // Bug 3756, when the image alone is displayed in the browser
  139.       // Mozilla is replacing the "alt" tag by "This image contains error"
  140.       // a clever hack, but we don't want to use that for the title
  141.       imageTitle = imageURL.split("/").pop();
  142.     }
  143.  
  144.     return this._getImageTransferable(imageURL, imageTitle, topUrl);
  145.   }
  146.  
  147.   // The URL bar favicon: appear as a XUL image but must be treated as a link
  148.   if ((nodeName == "image") || (nodeName == "xul:image")) {
  149.     return this._getLinkTransferable(topUrl, topTitle);
  150.   }
  151.  
  152.   // Browser tab: to be sent as a link
  153.   if ((nodeName == "tab") || (nodeName == "xul:tab")) {
  154.     var tabUrl = top.content.document.location.toString();
  155.     var tabTitle = node.label;
  156.  
  157.     return this._getLinkTransferable(tabUrl, tabTitle);
  158.   }
  159.  
  160.   // Mediabar
  161.   if ((nodeName == "photo") || (nodeName == "xul:photo")) {
  162.     return this._getMediabarTransferable(node, topUrl);
  163.   }
  164.  
  165.   // Do not support these
  166.   if ((nodeName == "shelficon") || (nodeName == "richtreefolder")) {
  167.     return null;
  168.   }
  169.  
  170.   // Deal with a selection
  171.   var selection = top.getSelection();
  172.  
  173.   if (selection.toString().length > 0) {
  174.     return this._getSelectionTransferable(topUrl, topTitle, selection);
  175.   }
  176.  
  177.   // If we don't find a SRC or HREF tag in this node, then it is most likely an
  178.   // inline element, so we will traverse through its ancestors until we find
  179.   // something we can use.
  180.   while (node && node != contentElement.contentDocument.body) {
  181.     if (node.src) {
  182.       // If it is an img, make sure the src attribute is the full path
  183.       node.setAttribute("src", node.src);
  184.       var imageURL = node.src;
  185.       var imageTitle = node.src;
  186.       if (node.getAttribute("title")) {
  187.         imageTitle = node.getAttribute("title");
  188.       } else if (node.getAttribute("alt")) {
  189.         imageTitle = node.getAttribute("alt");
  190.       }
  191.       return this._getImageTransferable(imageURL, imageTitle, topUrl);
  192.     }
  193.  
  194.     if (node.href) {
  195.       // If it is a link make sure the href attribute is the full path
  196.       node.setAttribute("href", node.href);
  197.       return this._getLinkTransferable(node.href, node.textContent);
  198.     }
  199.  
  200.     // Look at parent
  201.     node = node.parentNode;
  202.   }
  203.  
  204.   // Don't know... we can return a link to the page
  205.   return this._getLinkTransferable(topUrl, topTitle);
  206. }
  207.  
  208.  
  209. flockRichDragDropService.prototype.getMessageFromTransferable =
  210. function fRDDS_getMessageFromTransferable(aSubject, aListSize, aFlavorList)
  211. {
  212.   var message = {};
  213.  
  214.   // Get transferable's available flavors
  215.   var transFlavors = aSubject.flavorsTransferableCanExport();
  216.   // Build string array of flavors for easy compare
  217.   var transFlavorArray = [];
  218.   for (var i = 0; i < transFlavors.Count(); i++) {
  219.     transFlavorArray[i] = transFlavors.GetElementAt(i)
  220.                                       .QueryInterface(CI.nsISupportsCString)
  221.                                       .data;
  222.   }
  223.  
  224.   // Go through list of desired flavors
  225.   for (var i = 0; i < aListSize; i++) {
  226.     // Is this flavor available in the transferable?
  227.     var flavor = aFlavorList[i];
  228.     if (transFlavorArray.indexOf(flavor) != -1) {
  229.       // It is -- try to build a message using it
  230.       message = this._getMessageFromTransferableWithFlavor(aSubject, flavor);
  231.       if (message.body) {
  232.         // Successfully built a message from this transferable
  233.         return message;
  234.       }
  235.     }
  236.   }
  237.  
  238.   // None of the desired flavors are available in the transferable. If it has a
  239.   // flavor we support, use it instead.
  240.   for (var i = 0; i < transFlavorArray.length; i++) {
  241.     // Get a flavor from the transferable
  242.     var flavor = transFlavorArray[i];
  243.     // Try to build a message using it 
  244.     message = this._getMessageFromTransferableWithFlavor(aSubject, flavor);
  245.     if (message.body) {
  246.       // Successfully built a message from this transferable
  247.       return message;
  248.     }
  249.   }
  250.  
  251.   // No valid flavor available
  252.   _logger.debug("Could not find a supported flavor.");
  253.  
  254.   // We should do something to support it
  255.   throw "No Supported Flavour for Sharing";
  256. }
  257.  
  258.  
  259. flockRichDragDropService.prototype.getBreadcrumb =
  260. function fRDDS_getBreadcrumb(aType)
  261. {
  262.   var breadcrumb = ""; 
  263.  
  264.   // Is breadcrumb enabled?
  265.   var prefService = CC[FLOCK_PREF_SERVICE_CONTRACT_ID]
  266.                     .getService(CI.nsIPrefBranch);
  267.  
  268.   if (prefService.getPrefType("flock.sharing.breadcrumb.enabled")
  269.       && prefService.getBoolPref("flock.sharing.breadcrumb.enabled"))
  270.   {
  271.     var footer = flockGetString("common/sharing", "flock.sharing.breadcrumb.footer1");
  272.     var url = flockGetString("common/sharing", "flock.sharing.breadcrumb.url");
  273.  
  274.     // Build breadrcumb based on desired type of formatting
  275.     if (aType == "rich") {
  276.       breadcrumb = "<br /><br />" + footer + "<br />"
  277.                  + "<a href='" +url + "'>" + url + "</a>" + "<br />";
  278.     } else {
  279.       // aType == "plain"
  280.       breadcrumb = "\n\n" + footer + "\n" + url + "\n";
  281.     }
  282.   }
  283.  
  284.   return breadcrumb;
  285. }
  286.  
  287.  
  288. flockRichDragDropService.prototype.handleDrop =
  289. function frDDS_handleDrop(aSession, aTargetElement) {
  290.   var type = aTargetElement.localName.toLowerCase();
  291.  
  292.   if (type == "textarea") {
  293.     if (this._handleDropToTextArea(aSession, aTargetElement)) {
  294.       return true;
  295.     }
  296.   } else if (this._isInRichTextEditor(aTargetElement)) {
  297.     if (this._handleDropToRichTextEditor(aSession, aTargetElement)) {
  298.       return true;
  299.     }
  300.   }
  301.  
  302.   // Not handled - do something else?
  303.   return false;
  304. }
  305.  
  306. /**************************************************************************
  307.  * Flock Rich Drag Drop Service Private Members
  308.  **************************************************************************/
  309.  
  310. flockRichDragDropService.prototype._getMessageFromTransferableWithFlavor =
  311. function fRDDS_getMessageFromTransferableWithFlavor(aSubject, aFlavor) {
  312.   var message = {};
  313.   message.QueryInterface = function (aIID) {
  314.     if (aIID.equals(CI.flockIMessage) || aIID.equals(CI.nsISupports)) {
  315.       return this;
  316.     }
  317.     throw CR.NS_ERROR_NO_INTERFACE;
  318.   };
  319.  
  320.   // Get content from transferable
  321.   var content = this._getFlavourData(aSubject, aFlavor);
  322.   if (!content) {
  323.     return message;
  324.   }
  325.  
  326.   // Build message from content
  327.   var contentParts;
  328.   switch (aFlavor) {
  329.     case FLAVOR_MOZ_RDF:
  330.       var coop = CC["@flock.com/singleton;1"]
  331.                  .getService(CI.flockISingleton)
  332.                  .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  333.                  .wrappedJSObject;
  334.       var item = coop.get(content);
  335.       message.subject = item.name;
  336.       if (item.isInstanceOf(coop.FeedItem)) {
  337.         message.body = this._getFlavourData(aSubject, FLAVOR_HTML);
  338.       } else {
  339.         message.body = item.description;
  340.       }
  341.       break;
  342.  
  343.     case FLAVOR_FLOCK_MEDIA:
  344.     case FLAVOR_MOZ_URL:
  345.       contentParts = content.split("\n");
  346.       message.subject = contentParts[1];
  347.       message.body = contentParts[0];
  348.       break;
  349.  
  350.     case FLAVOR_TEXT_CITE:
  351.       contentParts = content.split("\n");
  352.       message.subject = contentParts[2];
  353.       message.body = content;
  354.       break;
  355.  
  356.     case FLAVOR_HTML_CITE:
  357.       message.subject = "";
  358.       message.body = content;
  359.       break;
  360.  
  361.     case FLAVOR_UNICODE:
  362.       contentParts = content.split(": ");
  363.       // Can parse if the ": " separator doesn't occur more than once
  364.       if (contentParts.length == 2) {
  365.         message.subject = contentParts[0];
  366.         message.body = contentParts[1];
  367.       } else {
  368.         message.subject = "";
  369.         message.body = content;
  370.       }
  371.       break;
  372.  
  373.     case FLAVOR_HTML:
  374.       message.subject = "";
  375.       message.body = content;
  376.       break;
  377.   }
  378.  
  379.   if (!message.body) {
  380.     _logger.debug("The flavor '"+aFlavor+"' is unsupported by this method");
  381.   }
  382.  
  383.   return message;
  384. }
  385.  
  386. // Used code from viewPartialSource.js
  387. flockRichDragDropService.prototype._selectionToHtml =
  388. function fRDDS__selectionToHtml(aSelection) {
  389.   var inst = this;
  390.  
  391.   /////////////////////////////////////////////////////////////////////////
  392.   // helper to get a path like FIXptr, but with an array
  393.   // instead of the "tumbler" notation
  394.   // see FIXptr:
  395.   // http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
  396.   function getPath(ancestor, node) {
  397.     var n = node;
  398.     var p = n.parentNode;
  399.     if (n == ancestor || !p) {
  400.       return null;
  401.     }
  402.     var path = [];
  403.     if (!path) {
  404.       return null;
  405.     }
  406.     do {
  407.       for (var i = 0; i < p.childNodes.length; i++) {
  408.         if (p.childNodes.item(i) == n) {
  409.           path.push(i);
  410.           break;
  411.         }
  412.       }
  413.       n = p;
  414.       p = n.parentNode;
  415.     } while (n != ancestor && p);
  416.     return path;
  417.   };
  418.  
  419.   function makeRelativeLinksAbsolute(aBaseUrl, aDOMNode) {
  420.     // This is a recursive method: bail if no tag name
  421.     if (!aDOMNode.tagName) {
  422.       return;
  423.     }
  424.  
  425.     switch (aDOMNode.tagName.toLowerCase()) {
  426.       case "img":
  427.         var url = aDOMNode.getAttribute("src");
  428.         if (!url.match("http")) {
  429.           aDOMNode.setAttribute("src", inst._makeURLAbsolute(aBaseUrl, url));
  430.         }
  431.         break;
  432.  
  433.       case "a":
  434.         var url = aDOMNode.getAttribute("href");
  435.         if (url && !url.match("http")) {
  436.           aDOMNode.setAttribute("href", inst._makeURLAbsolute(aBaseUrl, url));
  437.         }
  438.         break;
  439.  
  440.       default:
  441.         break
  442.     }
  443.     if (aDOMNode.hasChildNodes()) {
  444.       var kids = aDOMNode.childNodes;
  445.       for (var i = 0; i < kids.length; i++) {
  446.         makeRelativeLinksAbsolute(aBaseUrl, kids[i]);
  447.       }
  448.     }
  449.   };
  450.  
  451.   var range = aSelection.getRangeAt(0);
  452.   var ancestorContainer = range.commonAncestorContainer;
  453.   var doc = ancestorContainer.ownerDocument;
  454.  
  455.   var startContainer = range.startContainer;
  456.   var endContainer = range.endContainer;
  457.   var startOffset = range.startOffset;
  458.   var endOffset = range.endOffset;
  459.  
  460.   // let the ancestor be an element
  461.   if (ancestorContainer.nodeType == CI.nsIDOMNode.TEXT_NODE ||
  462.       ancestorContainer.nodeType == CI.nsIDOMNode.CDATA_SECTION_NODE)
  463.   {
  464.     ancestorContainer = ancestorContainer.parentNode;
  465.   }
  466.  
  467.   // for selectAll, let's use the entire document, including <html>...</html>
  468.   // @see DocumentViewerImpl::SelectAll() for how selectAll is implemented
  469.   try {
  470.     if (ancestorContainer == doc.body) {
  471.       ancestorContainer = doc.documentElement;
  472.     }
  473.   } catch (ex) {
  474.   }
  475.  
  476.   // each path is a "child sequence" (a.k.a. "tumbler") that
  477.   // descends from the ancestor down to the boundary point
  478.   var startPath = getPath(ancestorContainer, startContainer);
  479.   var endPath = getPath(ancestorContainer, endContainer);
  480.  
  481.   // clone the fragment of interest and reset everything to be relative to it
  482.   // note: it is with the clone that we operate from now on
  483.   ancestorContainer = ancestorContainer.cloneNode(true);
  484.   startContainer = ancestorContainer;
  485.   endContainer = ancestorContainer;
  486.   var i;
  487.   for (i = startPath ? startPath.length-1 : -1; i >= 0; i--) {
  488.     startContainer = startContainer.childNodes.item(startPath[i]);
  489.   }
  490.   for (i = endPath ? endPath.length-1 : -1; i >= 0; i--) {
  491.     endContainer = endContainer.childNodes.item(endPath[i]);
  492.   }
  493.  
  494.   // add special markers to record the extent of the selection
  495.   // note: |startOffset| and |endOffset| are interpreted either as
  496.   // offsets in the text data or as child indices (see the Range spec)
  497.   // (here, munging the end point first to keep the start point safe...)
  498.   var tmpNode;
  499.   if (endContainer.nodeType == CI.nsIDOMNode.TEXT_NODE ||
  500.       endContainer.nodeType == CI.nsIDOMNode.CDATA_SECTION_NODE)
  501.   {
  502.     // do some extra tweaks to try to avoid the view-source output to look like
  503.     // ...<tag>]... or ...]</tag>... (where ']' marks the end of selection).
  504.     // To get a neat output, the idea here is to remap the end point from:
  505.     // 1. ...<tag>]...   to   ...]<tag>...
  506.     // 2. ...]</tag>...  to   ...</tag>]...
  507.     if ((endOffset > 0 && endOffset < endContainer.data.length) ||
  508.         !endContainer.parentNode ||
  509.         !endContainer.parentNode.parentNode)
  510.     {
  511.       endContainer.insertData(endOffset, MARK_SELECTION_END);
  512.     } else {
  513.       tmpNode = doc.createTextNode(MARK_SELECTION_END);
  514.       endContainer = endContainer.parentNode;
  515.       if (endOffset == 0) {
  516.         endContainer.parentNode.insertBefore(tmpNode, endContainer);
  517.       } else {
  518.         endContainer.parentNode.insertBefore(tmpNode, endContainer.nextSibling);
  519.       }
  520.     }
  521.   } else {
  522.     tmpNode = doc.createTextNode(MARK_SELECTION_END);
  523.     endContainer.insertBefore(tmpNode, endContainer.childNodes.item(endOffset));
  524.   }
  525.  
  526.   if (startContainer.nodeType == CI.nsIDOMNode.TEXT_NODE ||
  527.       startContainer.nodeType == CI.nsIDOMNode.CDATA_SECTION_NODE)
  528.   {
  529.     // do some extra tweaks to try to avoid the view-source output to look like
  530.     // ...<tag>[... or ...[</tag>... (where '[' marks the start of selection).
  531.     // To get a neat output, the idea here is to remap the start point from:
  532.     // 1. ...<tag>[...   to   ...[<tag>...
  533.     // 2. ...[</tag>...  to   ...</tag>[...
  534.     if ((startOffset > 0 && startOffset < startContainer.data.length) ||
  535.         !startContainer.parentNode ||
  536.         !startContainer.parentNode.parentNode ||
  537.         startContainer != startContainer.parentNode.lastChild)
  538.     {
  539.       startContainer.insertData(startOffset, MARK_SELECTION_START);
  540.     } else {
  541.       tmpNode = doc.createTextNode(MARK_SELECTION_START);
  542.       startContainer = startContainer.parentNode;
  543.       if (startOffset == 0) {
  544.         startContainer.parentNode.insertBefore(tmpNode, startContainer);
  545.       } else {
  546.         startContainer.parentNode.insertBefore(tmpNode,
  547.                                                startContainer.nextSibling);
  548.       }
  549.     }
  550.   } else {
  551.     tmpNode = doc.createTextNode(MARK_SELECTION_START);
  552.     startContainer.insertBefore(tmpNode,
  553.                                 startContainer.childNodes.item(startOffset));
  554.   }
  555.  
  556.   // now extract, make url absolute and return the HTML source
  557.   tmpNode = doc.createElementNS(NS_XHTML, "div");
  558.   tmpNode.appendChild(ancestorContainer);
  559.   makeRelativeLinksAbsolute(doc.location, tmpNode);
  560.  
  561.   return tmpNode.innerHTML;
  562. }
  563.  
  564.  
  565. flockRichDragDropService.prototype._makeURLAbsolute =
  566. function fRDDS__makeURLAbsolute(aBase, aUrl) {
  567.   try {
  568.     var ioService = CC["@mozilla.org/network/io-service;1"]
  569.                     .getService(CI.nsIIOService);
  570.     var baseURI = ioService.newURI(aBase, null, null);
  571.  
  572.     return ioService.newURI(baseURI.resolve(aUrl), null, null).spec;
  573.   } catch (ex) {
  574.     // Special URLs throw exceptions, in this case we just return it "as is"
  575.     return aUrl;
  576.   }
  577. }
  578.  
  579.  
  580. flockRichDragDropService.prototype._makeHTMLCitation =
  581. function fRDDS__makeHTMLCitation(aUrl, aTitle, aContent) {
  582.   var source = <p class="citation">
  583.                  <cite>
  584.                    <a href={aUrl}>{aTitle}</a>
  585.                  </cite>
  586.                </p>;
  587.  
  588.   // We don't use E4X here because it would escape "aContent"
  589.   var result = "<blockquote cite=" + aUrl + ">"
  590.              + "<p>" + aContent + "</p>"
  591.              + "</blockquote>"
  592.              + source.toXMLString();
  593.  
  594.   return result;
  595. }
  596.  
  597.  
  598. flockRichDragDropService.prototype._makeTextCitation =
  599. function fRDDS__makeTextCitation(aUrl, aTitle, aContent) {
  600.   var result = aContent + "\n\n"
  601.              + aTitle + "\n"
  602.              + aUrl;
  603.  
  604.   return result;
  605. }
  606.  
  607.  
  608. flockRichDragDropService.prototype._addTextFlavour =
  609. function fRDDS__addTextFlavour(aTransferable,
  610.                                aFlavour,
  611.                                aTextData)
  612. {
  613.   var supports = CC["@mozilla.org/supports-string;1"]
  614.                  .createInstance(CI.nsISupportsString);
  615.   supports.data = aTextData;
  616.   aTransferable.addDataFlavor(aFlavour);
  617.   var length = supports.data.length * 2;
  618.   aTransferable.setTransferData(aFlavour, supports, length);
  619.  
  620.   return aTransferable;
  621. }
  622.  
  623.  
  624. flockRichDragDropService.prototype._getImageTransferable =
  625. function fRDDS__getImageTransferable(aImageUrl,
  626.                                      aImageTitle,
  627.                                      aWebPageUrl)
  628. {
  629.   var result = CC["@mozilla.org/widget/transferable;1"]
  630.                .createInstance(CI.nsITransferable);
  631.   
  632.   // text/x-moz-url
  633.   var urltext = ((aImageTitle.length) ? (aImageTitle + ":\n") : "")
  634.               + aWebPageUrl;
  635.   this._addTextFlavour(result, FLAVOR_MOZ_URL, urltext);
  636.  
  637.   // text/x-flock-media
  638.   var text = aImageUrl + "\n" + aImageTitle;
  639.   this._addTextFlavour(result, FLAVOR_FLOCK_MEDIA, text);
  640.  
  641.   // text/unicode
  642.   var text = ((aImageTitle.length) ? (aImageTitle + ": ") : "")
  643.            + aImageUrl;
  644.   this._addTextFlavour(result, FLAVOR_UNICODE, text);
  645.  
  646.   // text/html
  647.   var html = <a href={aWebPageUrl} title={aImageTitle}>
  648.                <img alt={aImageTitle} src={aImageUrl}/>
  649.              </a>;
  650.   this._addTextFlavour(result, FLAVOR_HTML, html.toXMLString());
  651.  
  652.   // text/x-flock-htmlcitation
  653.   var citation = this._makeHTMLCitation(aWebPageUrl, aImageTitle, html);
  654.   this._addTextFlavour(result, FLAVOR_HTML_CITE, citation);
  655.  
  656.   return result;
  657. }
  658.  
  659.  
  660. flockRichDragDropService.prototype._getLinkTransferable =
  661. function fRDDS__getLinkTransferable(aURL, aTitle) {
  662.   var result = CC["@mozilla.org/widget/transferable;1"]
  663.                .createInstance(CI.nsITransferable);
  664.   // text/x-moz-url
  665.   var urltext = aURL + "\n" + aTitle;
  666.   this._addTextFlavour(result, FLAVOR_MOZ_URL, urltext);
  667.  
  668.   // text/unicode
  669.   var text = aTitle + ": " + aURL;
  670.   this._addTextFlavour(result, FLAVOR_UNICODE, text);
  671.  
  672.   // text/html
  673.   var html = <a href={aURL}>{aTitle}</a>;
  674.   this._addTextFlavour(result, FLAVOR_HTML, html.toXMLString());
  675.  
  676.   return result;
  677. }
  678.  
  679.  
  680. flockRichDragDropService.prototype._getSelectionTransferable =
  681. function fRDDS__getSelectionTransferable(aURL, aTitle, aSelection) {
  682.   var result = CC["@mozilla.org/widget/transferable;1"]
  683.                .createInstance(CI.nsITransferable);
  684.  
  685.   // text/x-flock-htmlcitation
  686.   var htmlCitation = this._makeHTMLCitation(aURL, aTitle, html)
  687.   this._addTextFlavour(result, FLAVOR_HTML_CITE, htmlCitation);
  688.  
  689.   // text/x-flock-textcitation
  690.   var textCitation = this._makeTextCitation(aURL, aTitle, text)
  691.   this._addTextFlavour(result, FLAVOR_TEXT_CITE, textCitation);
  692.  
  693.   // text/unicode
  694.   var text = aTitle + ": " + aSelection.toString();
  695.   this._addTextFlavour(result, FLAVOR_UNICODE, text);
  696.  
  697.   // text/html
  698.   var html = this._selectionToHtml(aSelection);
  699.   this._addTextFlavour(result, FLAVOR_HTML, html);
  700.  
  701.   return result;
  702. }
  703.  
  704.  
  705. flockRichDragDropService.prototype._getMediabarTransferable =
  706. function fRDDS__getMediabarTransferable(aMedia, aTopUrl)
  707. {
  708.   var result = CC["@mozilla.org/widget/transferable;1"]
  709.                .createInstance(CI.nsITransferable);
  710.  
  711.   var title = aMedia.title.replace(/%20/g, " "); 
  712.   var url = aMedia.webPageUrl;
  713.   var imageUrl = aMedia.midSizePhoto;
  714.  
  715.   // text/x-moz-url
  716.   var urltext = ((title.length) ? (title + ":\n") : "") + aTopUrl;
  717.   this._addTextFlavour(result, FLAVOR_MOZ_URL, urltext);
  718.  
  719.   // text/x-flock-media
  720.   var text = url + "\n" + title;
  721.   this._addTextFlavour(result, FLAVOR_FLOCK_MEDIA, text);
  722.  
  723.   // text/unicode
  724.   var text = ((title.length) ? (title + ": ") : "") + url;
  725.   this._addTextFlavour(result, FLAVOR_UNICODE, text);
  726.  
  727.   // text/html
  728.   var html;
  729.   if (eval(aMedia.is_video)) {
  730.     // video
  731.     html = aMedia.buildHTML();
  732.     this._addTextFlavour(result, FLAVOR_HTML, html);
  733.   } else {
  734.     // photo
  735.     html = <a href={url} title={title}>
  736.              <img alt={title} src={imageUrl}/>
  737.            </a>;
  738.     this._addTextFlavour(result, FLAVOR_HTML, html.toXMLString());
  739.   }
  740.  
  741.   // text/x-flock-htmlcitation
  742.   var citation = this._makeHTMLCitation(aTopUrl, title, html);
  743.   this._addTextFlavour(result, FLAVOR_HTML_CITE, citation);
  744.  
  745.   return result;
  746. }
  747.  
  748.  
  749. // Retrieves the flavour data from the transferable
  750. flockRichDragDropService.prototype._getFlavourData =
  751. function fRDDS__getFlavourData(aTransferable, aFlavor) {
  752.   if (!aTransferable || !aFlavor) {
  753.     return "";
  754.   }
  755.  
  756.   var dataObj = {};
  757.   var len = {};
  758.  
  759.   // If the flavor is not in the transferable, then this call will throw
  760.   try {
  761.     aTransferable.getTransferData(aFlavor, dataObj, len);
  762.   } catch (ex) {
  763.     _logger.debug("The flavor '"+aFlavor+"' is unsupported by this transferable");
  764.     return "";
  765.   }
  766.  
  767.   return dataObj.value.QueryInterface(CI.nsISupportsString).data;
  768. }
  769.  
  770.  
  771. // Determine the appropriate action when dropping rich content onto a TEXTAREA
  772. // element.
  773. flockRichDragDropService.prototype._handleDropToTextArea =
  774. function frDDS__handleDropToTextArea(aSession, aTextarea) {
  775.   if (!(aSession && aTextarea)) {
  776.     return false;
  777.   }
  778.  
  779.   var catMgr = CC[FLOCK_CATEGORY_MANAGER_CONTRACT_ID]
  780.                .getService(CI.nsICategoryManager);
  781.   var svcEnum = catMgr.enumerateCategory(FLOCK_RICH_CONTENT_CATEGORY_ENTRY);
  782.   if (!svcEnum) {
  783.     return false;
  784.   }
  785.  
  786.   // Get the various rich content flavours
  787.   var trans = this.getRichSelection(aSession);
  788.  
  789.   // Find the appropriate supported service to drop the rich content on
  790.   while (svcEnum.hasMoreElements()) {
  791.     var entry = svcEnum.getNext().QueryInterface(CI.nsISupportsCString);
  792.     if (entry) {
  793.       var contractID = catMgr.getCategoryEntry(FLOCK_RICH_CONTENT_CATEGORY_ENTRY,
  794.                                                entry.data);
  795.       var service = CC[contractID].getService(CI.flockIRichContentDropHandler);
  796.       if (service.handleDrop(trans, aTextarea)) {
  797.         // Dropped the rich content on the appropriate supported service,
  798.         // no need to continue searching
  799.         return true;
  800.       }
  801.     }
  802.   }
  803.  
  804.   // Could not find the appropriate supported service to drop the rich
  805.   // context on, drop uber-flav
  806.   var uberFlavour = this._getUberFlavour(trans);
  807.   if (uberFlavour) {
  808.     this._addToTextarea(uberFlavour, aTextarea);
  809.     return true;
  810.   }
  811.  
  812.   // No uber-flavour either
  813.   return false;
  814. }
  815.  
  816.  
  817. // Determine the appropriate action when dropping rich content onto an element
  818. // within a rich text editor
  819. flockRichDragDropService.prototype._handleDropToRichTextEditor =
  820. function frDDS__handleDropToRichTextEditor(aSession, aTargetElement) {
  821.   if (!(aSession && aTargetElement)) {
  822.     return false;
  823.   }
  824.  
  825.   // Get the various rich content flavours
  826.   var transferable = this.getRichSelection(aSession);
  827.   if (!transferable) {
  828.     return false;
  829.   }
  830.  
  831.   // Get rich content
  832.   var content = this._getFlavourData(transferable, FLAVOR_HTML);
  833.   if (!content || this._isRichVideoContent(content)) {
  834.     // If we couldn't get the rich content or it's a video, just get plain text
  835.     // "Rich textareas" used in mail clients cannot accept HTML OBJECT or
  836.     // EMBED tags
  837.     content = this._getFlavourData(transferable, FLAVOR_UNICODE);
  838.     content = content.replace(/: /g, ":<br />");
  839.   }
  840.  
  841.   // Add it to the editor
  842.   if (content) {
  843.     this._addToRichTextEditor(content, aTargetElement);
  844.     return true;
  845.   }
  846.  
  847.   // We have failed
  848.   return false;
  849. }
  850.  
  851.  
  852. // Check if the content is rich video content
  853. flockRichDragDropService.prototype._isRichVideoContent =
  854. function fRDDS__isRichVideoContent(aContent) {
  855.   // Rich video content will be made up of:
  856.   // 1) an EMBED tag or
  857.   // 2) an EMBED tag within an OBJECT tag
  858.   return aContent && (aContent.toLowerCase().indexOf("<embed ") > -1);
  859. }
  860.  
  861.  
  862. // Get the uber flavour with the desired richness
  863. flockRichDragDropService.prototype._getUberFlavour =
  864. function fRDDS__getUberFlavour(aTransferable, aType) {
  865.   if (!aTransferable) {
  866.     return "";
  867.   }
  868.  
  869.   var uberFlavour = "";
  870.  
  871.   var newline = "\n";
  872.   if (aType == "rich") {
  873.     newline = "<br />";
  874.   }
  875.  
  876.   var link = this._getFlavourData(aTransferable, FLAVOR_UNICODE);
  877.   if (link) {
  878.     uberFlavour = flockGetString("common/sharing",
  879.                                  "flock.sharing.uberFlavour.link")
  880.                 + newline + link;
  881.   }
  882.  
  883.   var content = this._getFlavourData(aTransferable, FLAVOR_HTML);
  884.   if (content) {
  885.     if (uberFlavour) {
  886.       uberFlavour += newline + newline;
  887.     }
  888.  
  889.     uberFlavour += flockGetString("common/sharing",
  890.                                  "flock.sharing.uberFlavour.content")
  891.                  + newline + this._sanitizeHTMLContent(content);
  892.   }
  893.  
  894.   return uberFlavour;
  895. }
  896.  
  897.  
  898. flockRichDragDropService.prototype._sanitizeHTMLContent =
  899. function fRDDS__sanitizeHTMLContent(aContent) {
  900.   return aContent.replace(/&/g, "&");
  901. }
  902.  
  903.  
  904. flockRichDragDropService.prototype._isInRichTextEditor =
  905. function fRDDS__isInRichTextEditor(aElement) {
  906.   return aElement.ownerDocument.designMode == "on";
  907. }
  908.  
  909.  
  910. // Add the content to the text area object
  911. flockRichDragDropService.prototype._addToTextarea =
  912. function fRDDS__addToTextArea(aContent, aTextarea) {
  913.   if (aContent && aTextarea) {
  914.     var caretPos = aTextarea.selectionEnd;
  915.     var currentValue = aTextarea.value;
  916.     var breadcrumb = (aTextarea.value.length == caretPos)
  917.                    ? this.getBreadcrumb()
  918.                    : "";
  919.     aTextarea.value = currentValue.substring(0, caretPos)
  920.                     + aContent
  921.                     + currentValue.substring(caretPos)
  922.                     + breadcrumb;
  923.   }
  924. }
  925.  
  926.  
  927. // Add the content to the targeted elment in the rich text editor
  928. flockRichDragDropService.prototype._addToRichTextEditor =
  929. function fRDDS__addToRichTextEditor(aContent, aTargetElement) {
  930.   if (aContent && aTargetElement) {
  931.     var doc = aTargetElement.ownerDocument;
  932.     var targetName = aTargetElement.localName.toLowerCase();
  933.  
  934.     // Create SPAN element containing content
  935.     var contentElement = doc.createElement("span");
  936.     contentElement.innerHTML = aContent;
  937.  
  938.     if (targetName == "html" || targetName == "body") {
  939.       // Not any specific element within the body, so add at insertion point
  940.       doc.execCommand("insertHTML", false, aContent);
  941.     } else {
  942.       // Insert after target element
  943.       aTargetElement.parentNode.insertBefore(contentElement,
  944.                                              aTargetElement.nextSibling);
  945.     }
  946.  
  947.     // Check for breadcrumb -- we don't want to add more than one
  948.     if (!doc.getElementById(FLOCK_BREADCRUMB_ID)) {
  949.       // Append the breadcrumb to the body
  950.       var breadcrumbElement = doc.createElement("span");
  951.       breadcrumbElement.setAttribute("id", FLOCK_BREADCRUMB_ID);
  952.       breadcrumbElement.innerHTML = this.getBreadcrumb("rich");
  953.       doc.body.appendChild(breadcrumbElement);
  954.     }
  955.   }
  956. }
  957.  
  958. /**************************************************************************
  959.  * END Flock Rich Drag Drop Service Private Members
  960.  **************************************************************************/
  961.  
  962. /**************************************************************************
  963.  * END Flock Rich Drag Drop Service
  964.  **************************************************************************/
  965.  
  966. /**************************************************************************
  967.  * Helper Components
  968.  **************************************************************************/
  969.  
  970. const JS_SUBSCRIPT_LOADER_CONTRACTID = "@mozilla.org/moz/jssubscript-loader;1";
  971.  
  972. function loadSubScript(spec) {
  973.   var loader = CC[JS_SUBSCRIPT_LOADER_CONTRACTID]
  974.                .getService(CI.mozIJSSubScriptLoader);
  975.   var context = {};
  976.   loader.loadSubScript(spec, context);
  977.   return context;
  978. }
  979.  
  980. function loadLibraryFromSpec(aSpec) {
  981.   var loader = CC[JS_SUBSCRIPT_LOADER_CONTRACTID]
  982.                .getService(CI.mozIJSSubScriptLoader);
  983.   loader.loadSubScript(aSpec);
  984. }
  985.  
  986. loadLibraryFromSpec("chrome://flock/content/common/flocksafe.js");
  987.  
  988. /**************************************************************************
  989.  * XPCOM Support - Module Construction
  990.  **************************************************************************/
  991.  
  992. // Create array of components.
  993. var componentArray = [flockRichDragDropService];
  994.  
  995. // Wrap the components in a module.
  996. var module = new FlockXPCOMUtils.genericModule(MODULE_NAME, componentArray);
  997.  
  998. // Provide XPCOM with access to the module.
  999. function NSGetModule(aCompMgr, aFileSpec) {
  1000.   return module;
  1001. }
  1002.  
  1003. /**************************************************************************
  1004.  * END XPCOM Support
  1005.  **************************************************************************/
  1006.